/*******************************************************************************
** www.IfcGears.com                                                           **
** Copyright (C) 2010 - Bauhaus University Weimar, www.uni-weimar.de          **
** Written: 2010-10-02, Fabian Gerold, www.fabiangerold.de                    **
*******************************************************************************/

#include <assert.h>
#include <sstream>
#include <string>
#include "gears/IfcGears.h"
#include "schema/entity/Attribute.h"
#include "schema/entity/Entity.h"
#include "Type.h"

Type::Type( IfcGears* gears )
{
	m_gears = gears;
	m_classname = "";
	m_schema = "";
	m_header = "";

	m_super_primitive = "";
	m_super_primitive_min = 1;
	m_super_primitive_max = 1;
	m_supertype = NULL;

	m_reader_needs_object_map = false;
	m_typedef = false;
}

bool Type::setSchema( QString schema )
{
	m_schema = schema;
	return true;
}

void Type::setSchemaVersion( QString schema_version, shared_ptr<IfcRelease> ifc_release )
{
	m_schema_version = schema_version;
	m_ifc_release = ifc_release;
}

bool Type::parse()
{
	

	// extract header
	int pos_header_end;
	QRegExp rx_header_end( ";[\\r]?\\n" );
	pos_header_end = rx_header_end.indexIn( m_schema, 0 );

	if( pos_header_end > 0 )
	{
		QStringRef header_str_ref( &m_schema, 0, pos_header_end+rx_header_end.matchedLength() );
		QString header_str = header_str_ref.toString();
		m_header = header_str;

		int pos_supertype = 0;
		QRegExp rx_supertype( "=[\\s]*([a-zA-Z0-9_\\s\\(\\)\\[\\]\\:,\\-\"]*);" );
		if( (pos_supertype = rx_supertype.indexIn( header_str, 0 ) ) != -1 )
		{
			QString complete_match = rx_supertype.cap(0);
			QString supertype = rx_supertype.cap(1);
			supertype.remove( QRegExp( "^\\s*" ) );
			supertype.remove( QRegExp( "\\s*$" ) );
			m_base_datatype = supertype;
		}
		else
		{
			m_gears->slotEmitTxtOutWarning( "Type: could not find supertype in header: " + header_str );	
		}
	}
	else
	{
		m_gears->slotEmitTxtOutWarning( "Type: could not detect header end: " + m_schema );	
	}

	m_attribute_declaration = "";
	if( m_gears->m_basic_types.find( m_base_datatype ) != m_gears->m_basic_types.end() )
	{
		m_super_primitive = m_gears->m_basic_types[m_base_datatype];
		m_super_primitive_min = 1;
		m_super_primitive_max = 1;
		m_attribute_declaration = m_super_primitive + " m_value;\n";


		QRegExp rx_type_begin( "TYPE\\s+([a-zA-Z0-9_]+)\\s*=\\s*([a-zA-Z0-9_\\s\\(\\)\\[\\]\\:,\\-\"]+)\\s*;\\s*END_TYPE;" );

		if( rx_type_begin.indexIn( m_schema, 0 ) != -1 )
		{
			QString complete_match = rx_type_begin.cap(0);
			
			// contains no where statements
			//TYPE IfcAreaMeasure = REAL;
			//END_TYPE;

			m_typedef = true;
		}

	}
	else if( m_base_datatype.startsWith(  "ARRAY" ) || m_base_datatype.startsWith( "LIST" ) || m_base_datatype.startsWith( "SET" ) )
	{
		//ARRAY [1:4] OF REAL						-> ifc data: (5182.87,487.0,635272.05756)
		//LIST	[3:3] OF INTEGER					-> ifc data: (5182,487,5)
		//SET	[1:?] OF IfcRepresentationContext	-> ifc data: (#11,#33)

		int pos = 0;
		QRegExp rx( "(ARRAY|LIST|SET)\\s\\[([0-9]+):([0-9]+)\\]\\sOF\\s([a-zA-Z0-9_]+)" );
		if( (pos = rx.indexIn( m_base_datatype, 0 ) ) == -1 )
		{
			m_gears->slotEmitTxtOut( "couln't understand ARRAY/LIST supertype: " + m_base_datatype );
			return false;
		}
		QString complete_match = rx.cap(0);
		QString container_type = rx.cap(1);
		QString min = rx.cap(2);
		QString max = rx.cap(3);
		QString datatype = rx.cap(4);

		if( min.compare( "?" ) == 0 )
		{
			m_super_primitive_min = -1;
		}
		else
		{
			m_super_primitive_min = min.toInt();
		}
		
		if( max.compare( "?" ) == 0 )
		{
			m_super_primitive_max = -1;
		}
		else
		{
			m_super_primitive_max = max.toInt();
		}
		// todo: when max<=10 create single attributes instead of std::vector

		if( m_gears->m_basic_types.find( datatype ) != m_gears->m_basic_types.end() )
		{
			m_super_primitive = m_gears->m_basic_types[datatype];
		}
		else
		{
			m_gears->slotEmitTxtOutWarning( "Type: unhandled array supertype: " + datatype );	
		}
		m_attribute_declaration = "std::vector<" + m_super_primitive +"> m_vec;\n";


	}
	else if( m_base_datatype.startsWith( "STRING" ) )
	{
		// STRING(22) FIXED
		m_base_datatype = "STRING";
		m_super_primitive = "std::string";
		m_super_primitive_min = 1;
		m_super_primitive_max = 1;
		m_attribute_declaration = "std::string m_value;\n";

	}
	else if( m_base_datatype.startsWith( "ENUMERATION" ) )
	{
		// ENUMERATION OF (AHEAD ,BEHIND);
		int pos = 0;
		QRegExp rx( "ENUMERATION\\s*OF\\s*\\(([a-zA-Z0-9_,\\-\\r\\n\\s\"]+)\\)" );
		if( (pos = rx.indexIn( m_base_datatype, 0 ) ) == -1 )
		{
			m_gears->slotEmitTxtOutWarning( "couln't understand ENUMERATION supertype: " + m_base_datatype );
			return false;
		}
		QString complete_match = rx.cap(0);
		QString enum_values = rx.cap(1);
		enum_values.remove( QRegExp( "[\\r\\n\\s]" ) );
		m_super_enums = enum_values.split( "," ); 
		m_attribute_declaration = m_classname + "Enum m_enum;\n";


	}
	else if( m_base_datatype.startsWith( "SELECT" ) )
	{
		//SELECT	(IfcRatioMeasure	,IfcMeasureWithUnit	,IfcMonetaryMeasure);
		int pos = 0;
		QRegExp rx( "SELECT\\s*\\(([a-zA-Z0-9_,\\r\\n\\s]+)\\)" );
		if( (pos = rx.indexIn( m_base_datatype, 0 ) ) == -1 )
		{
			m_gears->slotEmitTxtOutWarning( "couln't understand SELECT supertype: " + m_base_datatype );
			return false;
		}
		QString complete_match = rx.cap(0);
		QString select_values = rx.cap(1);
		select_values.remove( QRegExp( "[\\r\\n\\s]" ) );
		m_select = select_values.split( "," );

		m_reader_needs_object_map = true;

	}
	else if( m_ifc_release->m_map_types.find( m_base_datatype ) != m_ifc_release->m_map_types.end() )
	{
		m_supertype = m_ifc_release->m_map_types[m_base_datatype];
	}
	else 
	{
		m_gears->slotEmitTxtOutWarning( "unknown base type: " + m_base_datatype );
		return false;
	}
	return true;
}

void Type::getAllSuperClasses( QStringList& set, bool gears_type )
{
	if( m_supertype != NULL )
	{
		set.append( m_supertype->m_classname );
		m_supertype->getAllSuperClasses( set );
	}

	if( m_gears->SELECT_DERIVE )
	{
		for( int j=0; j<m_select_supertypes.size(); ++j )
		{
			QString sel = m_select_supertypes.at( j );
			if( m_ifc_release->m_map_types.find(sel)!=m_ifc_release->m_map_types.end() )
			{
				shared_ptr<Type>  t = m_ifc_release->m_map_types[sel];
				set.append(t->m_classname);
				t->getAllSuperClasses(set);
			}
			if( m_ifc_release->m_map_entities.find(sel)!=m_ifc_release->m_map_entities.end() )
			{
				shared_ptr<Entity>  e = m_ifc_release->m_map_entities[sel];
				set.append(e->m_classname);
				e->getAllSuperClasses(set);
			}
		}
	}

	if( m_supertype == NULL )
	{
		if( m_select.size() > 0)
		{
			set.append("AbstractSelect");
		}
		else if( m_super_enums.size() > 0 )
		{
			set.append( "AbstractEnum" );
		}
		else
		{
			if( gears_type )
			{
				set.append("IfcGearsType");
			}
		}
	}
	set.removeDuplicates();
}

void Type::getAllSelectClasses( QStringList& set )
{
	foreach( QString sel, m_select )
	{
		if( m_ifc_release->m_map_types.find(sel)!=m_ifc_release->m_map_types.end() )
		{
			shared_ptr<Type>  t = m_ifc_release->m_map_types[sel];
			set.append(t->m_classname);
			t->getAllSelectClasses(set);
		}
		if( m_ifc_release->m_map_entities.find(sel)!=m_ifc_release->m_map_entities.end() )
		{
			shared_ptr<Entity>  e = m_ifc_release->m_map_entities[sel];
			set.append(e->m_classname);
			e->getAllSelectClasses(set);
		}
	}
}

void Type::linkDependencies()
{
	for( int j=0; j<m_select.size(); ++j )
	{
		QString sel = m_select.at( j );
		if( m_ifc_release->m_map_types.find(sel)!=m_ifc_release->m_map_types.end() )
		{
			shared_ptr<Type>  t = m_ifc_release->m_map_types[sel];
			t->m_select_supertypes.append(m_classname);
		}
		if( m_ifc_release->m_map_entities.find(sel)!=m_ifc_release->m_map_entities.end() )
		{
			shared_ptr<Entity>  e = m_ifc_release->m_map_entities[sel];
			e->m_select_supertypes.append(m_classname);
		}
	}
}

QString Type::getCodeHeader()
{
	m_select_supertypes.removeDuplicates();
	QStringList set_select_superclasses;
	QString type_inherit;
	QString additional_includes;

	if( m_select_supertypes.size() > 0 )
	{
		QStringList direct_base_classes;

		if( m_supertype != NULL )
		{
			direct_base_classes.append(m_supertype->m_classname);
		}

		direct_base_classes.append(m_select_supertypes);
		getAllSuperClasses(set_select_superclasses);

		QStringList derived_from;
		foreach( QString sel, direct_base_classes)
		{
			// check if select base type is already base of other base type
			bool already_other_base = false;
			foreach( QString other_base, set_select_superclasses)
			{
				if( other_base.compare(sel)==0 )
				{
					continue;
				}

				QStringList base_classes_of_other_base;
				if( m_ifc_release->m_map_types.find(other_base)!=m_ifc_release->m_map_types.end())
				{
					shared_ptr<Type>  t = m_ifc_release->m_map_types[other_base];

					t->getAllSuperClasses(base_classes_of_other_base);
				}
				else if( m_ifc_release->m_map_entities.find(other_base)!=m_ifc_release->m_map_entities.end())
				{
					shared_ptr<Entity>  e = m_ifc_release->m_map_entities[other_base];
					e->getAllSuperClasses(base_classes_of_other_base);
				}

				if( base_classes_of_other_base.contains(other_base) )
				{
					already_other_base = true;
				}
			}
			
			if( already_other_base )
			{
				continue;
			}

			if( !derived_from.contains(sel) )
			{
				if( type_inherit.size() > 0 )
				{
					type_inherit += ", public ";
				}
				else
				{
					type_inherit += " : public ";
				}
				type_inherit += sel;

				if( m_ifc_release->m_map_types.find(sel)!=m_ifc_release->m_map_types.end())
				{
					shared_ptr<Type>  t = m_ifc_release->m_map_types[sel];
					additional_includes += "#include \"" + t->m_classname + ".h\"\n";
					t->getAllSuperClasses(derived_from);
				}
				else if( m_ifc_release->m_map_entities.find(sel)!=m_ifc_release->m_map_entities.end())
				{
					shared_ptr<Entity>  e = m_ifc_release->m_map_entities[sel];

					additional_includes += "#include \"" + e->m_classname + ".h\"\n";
					e->getAllSuperClasses(derived_from);
				}
			}
		}
	}
	else
	{
		if( m_supertype != NULL )
		{
			additional_includes += "#include \"" + m_supertype->m_classname + ".h\"\n";

			if( type_inherit.size() > 0 )
			{
				type_inherit += ", public " + m_supertype->m_classname;
			}
			else
			{
				type_inherit += " : public " + m_supertype->m_classname;
			}
		}
	}
	
	if( type_inherit.size() == 0 )
	{
		if( m_select.size() > 0)
		{
			type_inherit = " : public AbstractSelect";
		}
		else if( m_super_enums.size() > 0 )
		{
			type_inherit = " : public AbstractEnum";
		}
		else
		{
			type_inherit = " : public IfcGearsType";
		}
	}

	QString reader_args = "std::string& arg";

	if( m_reader_needs_object_map )
	{
		QString namesp;
//		if( m_gears->m_namespace.size() > 0 )namesp = m_gears->m_namespace + "::";
		reader_args = "std::string& arg, const std::map<int,shared_ptr<" + namesp + "IfcGearsEntity> >& map";
	}
	
	if( m_select.size() > 0 )
	{
		// abstract class
		QString code_h( m_gears->m_header_comment );
		code_h += "\n#pragma once\n";
		code_h += "#include <iostream>\n";
		code_h += "#include <sstream>\n";
		code_h += "#include <map>\n";
		code_h += "#include \"ifcgears/model/shared_ptr.h\"\n";
		code_h += "#include \"ifcgears/model/IfcGearsObject.h\"\n";
		code_h += additional_includes;
		code_h += "\n";
		
		if( m_gears->m_ifc_namespace )
			code_h += "namespace " + m_schema_version + "\n{\n";
		code_h += "// " + m_header.replace( '\n', "" ) + "\n";
		code_h += "class " + m_classname + type_inherit + "\n{\npublic:\n";

		// default constructor
		code_h += m_classname + "();\n";
		code_h += "~" + m_classname + "();\n";
		code_h += "virtual const char* classname() { return \"" + m_classname + "\"; }\n";
		code_h += "virtual void getStepParameter( std::stringstream& stream, bool is_select_type = false ) const = 0; // abstract class\n";
		if( m_gears->m_ifc_namespace )
			code_h += "static shared_ptr<" + m_schema_version + "::" + m_classname + "> readStepData( " + reader_args + " );\n";
		else
			code_h += "static shared_ptr<" + m_classname + "> readStepData( " + reader_args + " );\n";

		code_h += "};\n";
		if( m_gears->m_ifc_namespace )
			code_h += "}\n";
		code_h += "\n";
		
		return code_h;
	}
	
	// class is not abstract. if not yet derived from IfcGearsType, derive from it
	if( !type_inherit.contains( "IfcGearsType" ) )
	{
		QStringList set_all_superclasses;
		getAllSuperClasses( set_all_superclasses, false );

		if( !set_all_superclasses.contains( "IfcGearsType" ) )
		{
			if( type_inherit.size() > 0 )
			{
				type_inherit += ", public IfcGearsType";
			}
			else
			{
				type_inherit += " : public IfcGearsType";
			}
		}
	}

	// enums
	QString enums;
	if( m_super_enums.size() > 0 )
	{
		enums += "enum " + m_classname + "Enum\n";
		enums += "{\n";
		for( int i=0; i<m_super_enums.size(); ++i )
		{
			if( i>0 )
			{
				enums		+= ",\n";
			}
			QString en = m_super_enums[i];
			if( en.compare( "NULL" ) == 0 )
			{
				// cpp doesn't accept NULL as enum value
				en = "ENUM_NULL";
			}
			enums += "ENUM_" + en;
		}
		enums += "};\n\n";
	}


	QString code_h( m_gears->m_header_comment );
	code_h += "\n#pragma once\n";
	code_h += "#include <vector>\n";
	code_h += "#include <map>\n";
	code_h += "#include <sstream>\n";
	code_h += "#include <string>\n";
	code_h += "#include \"ifcgears/model/shared_ptr.h\"\n";
	code_h += "#include \"ifcgears/model/IfcGearsObject.h\"\n";
	code_h += additional_includes;

	code_h += "\n";

	if( m_gears->m_ifc_namespace )
		code_h += "namespace " + m_schema_version + "\n{\n";

	code_h += "// " + m_header.replace( '\n', "" ) + "\n";
	code_h += "class " + m_classname + type_inherit + "\n{\npublic:\n";

	code_h += enums;

	// default constructor
	code_h += m_classname + "();\n";

	// supertype primitive constructor
	if( m_gears->m_basic_types.find( m_base_datatype ) != m_gears->m_basic_types.end() )
	{
		code_h += m_classname + "( " + m_super_primitive + " value );\n";
	}

	// enum constructor
	if( m_super_enums.size() > 0 )
	{
		code_h += m_classname + "( " + m_classname + "Enum e ) { m_enum = e; }\n";
	}

	// destructor
	code_h += "~" + m_classname + "();\n";

	code_h += "virtual const char* classname() { return \"" + m_classname + "\"; }\n";
	code_h += "virtual void getStepParameter( std::stringstream& stream, bool is_select_type = false ) const;\n";
	code_h += "static shared_ptr<" + m_classname + "> readStepData( " + reader_args + " );\n";

	// attributes
	code_h += m_attribute_declaration;

	code_h += "};\n";
	if( m_gears->m_ifc_namespace )
		code_h += "}\n"; // namespace

	code_h += "\n";
	
	return code_h;
}

QString Type::getCodeCpp()
{
	QString code_cpp( m_gears->m_header_comment );
	
	if( m_select.size()> 0)
	{
		code_cpp += "#include <map>\n";
		code_cpp += "#include \"ifcgears/model/shared_ptr.h\"\n";
		code_cpp += "#include \"ifcgears/model/IfcGearsException.h\"\n";
		code_cpp += "#include \"ifcgears/reader/ReaderUtil.h\"\n";

		QStringList set_select_superclasses;

		getAllSelectClasses(set_select_superclasses);
		set_select_superclasses.removeDuplicates();
		bool first_if = true;
		foreach(QString sel, set_select_superclasses)
		{

			// only TYPE obects are inline
			if( m_ifc_release->m_map_types.find(sel) == m_ifc_release->m_map_types.end())
			{
				continue;
			}

			code_cpp += "#include \"include/" + sel + ".h\"\n";
		}

	}
	else
	{
		code_cpp += "\n#include <sstream>\n";
		code_cpp += "#include <limits>\n";
		code_cpp += "#include <map>\n";
		code_cpp += "#include \"ifcgears/reader/ReaderUtil.h\"\n";
		code_cpp += "#include \"ifcgears/writer/WriterUtil.h\"\n";
		code_cpp += "#include \"ifcgears/model/shared_ptr.h\"\n";
		code_cpp += "#include \"ifcgears/model/IfcGearsException.h\"\n";
	}

	for( int j=0; j<m_select_supertypes.size(); ++j )
	{
		QString sel = m_select_supertypes.at( j );
		code_cpp += "#include \"include/" + sel + ".h\"\n";
	}

	code_cpp += "#include \"include/" + m_classname + ".h\"\n";

	code_cpp += "\n";

	// comment with class name
	code_cpp += "// TYPE " + m_classname + " ";
	while( code_cpp.length() <= 80 )
	{
		code_cpp.append( "/" );
	}
	code_cpp += "\n";

	// constructors
	if( m_gears->m_ifc_namespace )
		code_cpp += m_schema_version + "::" + m_classname + "::" + m_classname + "() {}\n";
	else
		code_cpp += m_classname + "::" + m_classname + "() {}\n";

	if( m_gears->m_basic_types.find( m_base_datatype ) != m_gears->m_basic_types.end() )
	{
		if( m_gears->m_ifc_namespace )
			code_cpp += m_schema_version + "::" + m_classname + "::" + m_classname + "( " + m_super_primitive + " value ) { m_value = value; }\n";
		else
			code_cpp += m_classname + "::" + m_classname + "( " + m_super_primitive + " value ) { m_value = value; }\n";
	}
	if( m_gears->m_ifc_namespace )
		code_cpp += m_schema_version + "::" + m_classname + "::~" + m_classname + "() {}\n";
	else
		code_cpp += m_classname + "::~" + m_classname + "() {}\n";

	// step writer
	if( m_select.size()> 0)
	{

	}
	else
	{
		if( m_gears->m_ifc_namespace )
			code_cpp += "void " + m_schema_version + "::" + m_classname + "::getStepParameter( std::stringstream& stream, bool is_select_type ) const\n{\n";
		else
			code_cpp += "void " + m_classname + "::getStepParameter( std::stringstream& stream, bool is_select_type ) const\n{\n";
			
		code_cpp += getCodeWriterType();
		code_cpp += "}\n";
	}

	QString reader_args = "std::string& arg";
	//if( need_map )
	if( m_reader_needs_object_map )
	{
		//QString namesp;
		//if( m_gears->m_namespace.size() > 0 )namesp = m_gears->m_namespace + "::";
		reader_args = "std::string& arg, const std::map<int,shared_ptr<IfcGearsEntity> >& map";
	}

	if( m_gears->m_ifc_namespace )
	{
		code_cpp += "shared_ptr<" + m_schema_version + "::" + m_classname + "> " + m_schema_version + "::" + m_classname + "::readStepData( " + reader_args + " )\n{\n";		
	}
	else
	{
		code_cpp += "shared_ptr<" + m_classname + "> " + m_classname + "::readStepData( " + reader_args + " )\n{\n";
	}
	
	code_cpp += getTypeReader("");
	code_cpp += "}\n";

	return code_cpp;
}


int Type::getNumAttributesIncludingSupertypes()
{
	int num = 1;
	if( m_supertype != NULL )
	{
		num += m_supertype->getNumAttributesIncludingSupertypes();
	}
	
	return num;
}

QString Type::getClassDeclaration()
{
	return "class " + m_classname + ";\n";
}

QString Type::getTypeReader( QString obj_name )
{
	QString type_reader = "	// read TYPE\n";

	int supertype_count=0;
	if( m_supertype != 0 )				++supertype_count;
	if( m_super_primitive.size() > 0 )	++supertype_count;
	if( m_super_enums.size() > 0 )		++supertype_count;
	if( m_select.size() > 0 )		++supertype_count;

	if( supertype_count > 1 )
	{
		m_gears->slotEmitTxtOutWarning( "Type::getTypeParser: more than one supertype detected: " + m_schema );
	}
	if( supertype_count == 0 )
	{
		m_gears->slotEmitTxtOutWarning( "Type::getTypeParser: no supertype detected: " + m_schema );
	}

	QString reader_return;
	if( obj_name.size() == 0 )
	{
		// types have only one attribute
		obj_name = "type_object";
		type_reader += "if( arg.compare( \"$\" ) == 0 ) { return shared_ptr<" + m_classname + ">(); }\n";
		type_reader +=  "shared_ptr<" + m_classname + "> " + obj_name + "( new " + m_classname + "() );\n";
		
		reader_return = "return " + obj_name+ ";\n";
	}

	if( m_supertype != 0 )
	{
		type_reader += m_supertype->getTypeReader( obj_name );

	}
	else if( m_super_primitive.size() > 0 )
	{
		if( m_super_primitive_min == 1 && m_super_primitive_max == 1 )
		{
			
			type_reader += "//supertype as attribute: " + m_super_primitive + " m_value\n";
			

			if( m_super_primitive.compare( "bool" ) == 0 )
			{
				type_reader += "if( _stricmp( arg.c_str(), \".F.\" ) == 0 )\n";
				type_reader += "{\n";
				type_reader += "" + obj_name + "->m_value = false;\n";
				type_reader += "}\n";
				type_reader += "else if( _stricmp( arg.c_str(), \".T.\" ) == 0 )\n";
				type_reader += "{\n";
				type_reader += "" + obj_name + "->m_value = true;\n";
				type_reader += "}\n";

			}
			else if( m_super_primitive.compare( "int" ) == 0 )
			{
				type_reader +=  obj_name + "->m_value = atoi( arg.c_str() );\n";
			}
			else if( m_super_primitive.compare( "double" ) == 0 )
			{
				type_reader +=  obj_name + "->m_value = atof( arg.c_str() );\n";
			}
			else if( m_super_primitive.compare( "std::string" ) == 0 )
			{
				type_reader +=  obj_name + "->m_value = arg;\n";
			}
		}
		else
		{
			// vector of 
			type_reader += "//supertype as attribute: std::vector<" + m_super_primitive + "> m_vec\n";

			if( m_super_primitive.compare( "bool" ) == 0 )
			{
				type_reader += "readBoolList(  arg, " + obj_name + "->m_vec );\n";
				type_reader += "writeBoolList(  stream, " + obj_name + "->m_vec );\n";
				
			}
			else if( m_super_primitive.compare( "int" ) == 0 )
			{
				type_reader += "readIntList(  arg, " + obj_name + "->m_vec );\n";
			}
			else if( m_super_primitive.compare( "double" ) == 0 )
			{
				type_reader += "readDoubleList( arg, " + obj_name + "->m_vec );\n";
			}
			else if( m_super_primitive.compare( "const char*" ) == 0 )
			{
				type_reader += "readConstCharList( arg, " + obj_name + "->m_vec );\n";
			}
			else if( m_super_primitive.compare( "std::string" ) == 0 )
			{
				type_reader += "readStringList( arg, " + obj_name + "->m_vec );\n";
			}
		}

	}
	else if( m_super_enums.size() > 0 )
	{
		for( int j=0; j<m_super_enums.size(); ++j )
		{
			QString en = m_super_enums.at( j );

			if( en.compare( "NULL" ) == 0 )
			{
				// cpp doesn't accept NULL as enum value
				en = "ENUM_NULL";
			}

			type_reader += "";
			if( j > 0 )
			{
				type_reader += "else ";
			}
			type_reader += "if( _stricmp( arg.c_str(), \"." + en + ".\" ) == 0 )\n";
			type_reader += "{\n";
			type_reader += "" + obj_name + "->m_enum = " + m_classname + "::ENUM_" + en + ";\n";
			type_reader += "}\n";
		}
		
	}
	else if( m_select.size() > 0 )
	{
		type_reader = "// Read SELECT TYPE\n";
		type_reader += "if( arg.size() == 0 ){ return shared_ptr<" + m_classname+">(); }\n";
		type_reader += "if( arg[0] == '#' )\n";
		type_reader += "{\n";
		type_reader += "int id=atoi( arg.substr(1,arg.length()-1).c_str() );\n";
		type_reader += "std::map<int,shared_ptr<IfcGearsEntity> >::const_iterator it_ifc_obj = map.find( id );\n";
		type_reader += "if( it_ifc_obj != map.end() )\n";
		type_reader += "{\n";
		type_reader += "shared_ptr<" + m_classname+"> " + obj_name + " = dynamic_pointer_cast<"+m_classname+">((*it_ifc_obj).second);\n";
		type_reader += "return " + obj_name + ";\n";
		type_reader += "}\n";
		type_reader += "else\n";
		type_reader += "{\n";
		type_reader += "std::stringstream strs;\n";
		type_reader += "strs << \"Object width id \" << id << \" not found\";\n";
		type_reader += "throw IfcGearsException( strs.str() );\n";
		type_reader += "}\n";
		type_reader += "}\n";
		type_reader += "else if( arg.compare(\"$\")==0 )\n";
		type_reader += "{\n";
		type_reader += " return shared_ptr<" + m_classname+">();\n";
		type_reader += "}\n";
		type_reader += "else if( arg.compare(\"*\")==0 )\n";
		type_reader += "{\n";
		type_reader += " return shared_ptr<" + m_classname+">();\n";
		type_reader += "}\n";
		type_reader += "else\n{\n";

		type_reader += "// inline arguments\n";


		QStringList set_select_superclasses;
		getAllSelectClasses(set_select_superclasses);
		set_select_superclasses.removeDuplicates();

		if( set_select_superclasses.size() > 0 )
		{
			type_reader += "std::string keyword;\n";
			type_reader += "std::string inline_arg;\n";
			type_reader += "tokenizeInlineArgument( arg, keyword, inline_arg );\n";

			bool first_if = true;
			foreach(QString sel, set_select_superclasses)
			{
				// only TYPE obects are inline
				if( m_ifc_release->m_map_types.find(sel) == m_ifc_release->m_map_types.end())
				{
					continue;
				}

				shared_ptr<Type>  t = m_ifc_release->m_map_types[sel];
				bool inline_arg_reader_needs_map = false;
				if( t->m_reader_needs_object_map )
				{
					inline_arg_reader_needs_map = true;
				}

				if( !first_if )
				{
					type_reader += "else ";
				}
				first_if = false;
				type_reader += "if( keyword.compare(\"" + sel.toUpper() + "\")== 0 )\n";
				type_reader += "{\n";

				if( inline_arg_reader_needs_map )
				{
					type_reader += "return " + sel + "::readStepData( inline_arg, map );\n";
				}
				else
				{
					type_reader += "return " + sel + "::readStepData( inline_arg );\n";
				}

				type_reader += "}\n";
			}
		}

#ifdef READ_PLAIN_INLINE_ARGUMENTS
		type_reader += "// single argument without identifier, try which one reads successfully\n";
		//shared_ptr<IfcTranslationalStiffnessSelect> select_IfcBoolean = IfcBoolean::readStepData( inline_arg );
		//if( select_IfcBoolean ) return select_IfcBoolean;
		foreach(QString sel, set_select_superclasses)
		{
			// only TYPE obects are inline
			if( m_gears->m_map_types.find(sel) == m_gears->m_map_types.end())
			{
				continue;
			}

			shared_ptr<Type>  t = m_gears->m_map_types[sel];
			bool inline_arg_reader_needs_map = false;
			if( t->m_reader_needs_object_map )
			{
				inline_arg_reader_needs_map = true;
			}

			if( !first_if )
			{
				type_reader += "else ";
			}
			first_if = false;
			type_reader += "shared_ptr<" + sel + "> select_" + sel + " = ";
			if( inline_arg_reader_needs_map )
			{
				type_reader += "return " + sel + "::readStepData( inline_arg, map );\n";
			}
			else
			{
				type_reader += "return " + sel + "::readStepData( inline_arg );\n";
			}
		}
#endif
		
		type_reader += "std::stringstream strs;\n";
		type_reader += "strs << \"unhandled inline argument: \" << arg << \" in function " + m_schema_version + "::" + m_classname + "::readStepData\" << std::endl;\n";
		type_reader += "throw IfcGearsException( strs.str() );\n";

		type_reader += "}\n";
		if( m_gears->m_ifc_namespace )
			type_reader += "return shared_ptr<" + m_schema_version + "::" + m_classname+">();\n";
		else
			type_reader += "return shared_ptr<" + m_classname+">();\n";
		return type_reader;
	}
	else
	{
		m_gears->slotEmitTxtOutWarning( "Type: no attributes and no supertype detected: " + m_schema );
	}
	type_reader += reader_return;
	return type_reader;
}

QString getWriterForElementary(QString elementary_type, int min, int max)
{
	QString elementary_writer;
	if( min == 1 && max == 1 )
	{
		elementary_writer += "//supertype as attribute: " + elementary_type + " m_value\n";

		if( elementary_type.compare( "bool" ) == 0 )
		{
			elementary_writer += "	if( m_value == false )\n";
			elementary_writer += "	{\n";
			elementary_writer += "		stream << \".F.\";\n";
			elementary_writer += "	}\n";
			elementary_writer += "	else if( m_value == true )\n";
			elementary_writer += "	{\n";
			elementary_writer += "		stream << \".T.\";\n";
			elementary_writer += "	}\n";
		}
		else if( elementary_type.compare( "int" ) == 0 )
		{
			elementary_writer += "	stream << m_value;\n";
		}
		else if( elementary_type.compare( "double" ) == 0 )
		{
			elementary_writer += "	stream << m_value;\n";
		}
		else if( elementary_type.compare( "std::string" ) == 0 )
		{
			elementary_writer += "	stream << \"\'\" << m_value << \"\'\";\n";
		}
	}
	else
	{
		// vector of 
		elementary_writer += "	//supertype as attribute: std::vector<" + elementary_type + "> m_vec\n";

		if( elementary_type.compare( "bool" ) == 0 )
		{
			elementary_writer += "writeBoolList( stream, m_vec );\n";
		}
		else if( elementary_type.compare( "int" ) == 0 )
		{
			elementary_writer += "writeIntList( stream, m_vec );\n";
		}
		else if( elementary_type.compare( "double" ) == 0 )
		{
			elementary_writer += "writeDoubleList( stream, m_vec );\n";
		}
		else if( elementary_type.compare( "const char*" ) == 0 )
		{
			elementary_writer += "writeConstCharList( stream, m_vec );\n";
		}
		else if( elementary_type.compare( "std::string" ) == 0 )
		{
			elementary_writer += "writeStringList( stream, m_vec );\n";
		}
	}
	return elementary_writer;
}

QString Type::getCodeWriterType()
{
	if( m_select.size() > 0 )
	{
		// select classes are abstract and do not implement getStepData
		return "";
	}

	QString code_writer = "";
	
	//if( m_select.size() > 0 )
	//{
	//	code_writer += "	stream << \"" + m_classname.toUpper() + "(\";\n";
	//}

	//if( m_select.size() > 0 )
	{
		code_writer += "if( is_select_type ) { stream << \"" + m_classname.toUpper() + "(\"; }\n";
	}
	
	if( m_supertype != 0 )
	{
		if( m_supertype->m_super_primitive.size() > 0 )
		{
			code_writer += getWriterForElementary( m_supertype->m_super_primitive, m_supertype->m_super_primitive_min, m_supertype->m_super_primitive_max );
		}
		else
		{
			throw std::exception("unhandled supertype writer");
		}
		
	}
	else if( m_super_primitive.size() > 0 )
	{
		code_writer += getWriterForElementary( m_super_primitive, m_super_primitive_min, m_super_primitive_max );
	}
	else if( m_super_enums.size() > 0 )
	{
		for( int j=0; j<m_super_enums.size(); ++j )
		{
			QString en = m_super_enums.at( j );

			if( en.compare( "NULL" ) == 0 )
			{
				// cpp doesn't accept NULL as enum value
				en = "ENUM_NULL";
			}

			code_writer += "\t";
			if( j > 0 )
			{
				code_writer += "else ";
			}

			code_writer += "if( m_enum == ENUM_" + en + " )\n";
			code_writer += "{\n";
			code_writer += "stream << \"." + en + ".\";\n";
			code_writer += "}\n";	
		}
	}
	else
	{
		m_gears->slotEmitTxtOutWarning( "Type: no attributes and no supertype detected: " + m_schema );
	}

	//if( m_select.size() > 0 )
	{
		code_writer += "if( is_select_type ) { stream << \")\"; }\n";
	}
	return code_writer;
}
